package com.android.fxcontacts;

import java.util.ArrayList;

import android.content.Context;
import android.net.Uri;
import android.os.Parcel;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;

public abstract class ContactEntryAdapter<E extends ContactEntryAdapter.Entry>
		extends BaseAdapter
{
	protected ArrayList<ArrayList<E>> mSections;
	protected LayoutInflater mInflater;
	protected Context mContext;
	protected boolean mSeparators;

	/**
	 * Base class for adapter entries.
	 */
	public static class Entry
	{
		public int type = -1;
		public String label;
		public String data;
		public Uri uri;
		public long id = 0;
		public long contactId;
		public int maxLines = 1;
		public String mimetype;

		/**
		 * Helper for making subclasses parcelable.
		 */
		protected void writeToParcel(Parcel p)
		{
			p.writeInt(type);
			p.writeString(label);
			p.writeString(data);
			p.writeParcelable(uri, 0);
			p.writeLong(id);
			p.writeInt(maxLines);
			p.writeString(mimetype);
		}

		/**
		 * Helper for making subclasses parcelable.
		 */
		protected void readFromParcel(Parcel p)
		{
			final ClassLoader loader = getClass().getClassLoader();
			type = p.readInt();
			label = p.readString();
			data = p.readString();
			uri = p.readParcelable(loader);
			id = p.readLong();
			maxLines = p.readInt();
			mimetype = p.readString();
		}
	}

	ContactEntryAdapter(Context context, ArrayList<ArrayList<E>> sections,
			boolean separators)
	{
		mContext = context;
		mInflater = (LayoutInflater) context
				.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		mSections = sections;
		mSeparators = separators;
	}

	/**
	 * Resets the section data.
	 * 
	 * @param sections
	 *            the section data
	 */
	public final void setSections(ArrayList<ArrayList<E>> sections,
			boolean separators)
	{
		mSections = sections;
		mSeparators = separators;
		notifyDataSetChanged();
	}

	/**
	 * Resets the section data and returns the position of the given entry.
	 * 
	 * @param sections
	 *            the section data
	 * @param entry
	 *            the entry to return the position for
	 * @return the position of entry, or -1 if it isn't found
	 */
	public final int setSections(ArrayList<ArrayList<E>> sections, E entry)
	{
		mSections = sections;
		notifyDataSetChanged();

		int numSections = mSections.size();
		int position = 0;
		for (int i = 0; i < numSections; i++)
		{
			ArrayList<E> section = mSections.get(i);
			int sectionSize = section.size();
			for (int j = 0; j < sectionSize; j++)
			{
				E e = section.get(j);
				if (e.equals(entry))
				{
					position += j;
					return position;
				}
			}
			position += sectionSize;
		}
		return -1;
	}

	/**
	 * @see android.widget.ListAdapter#getCount()
	 */
	public final int getCount()
	{
		return countEntries(mSections, mSeparators);
	}

	/**
	 * @see android.widget.ListAdapter#hasSeparators()
	 */
	@Override
	public final boolean areAllItemsEnabled()
	{
		return mSeparators == false;
	}

	/**
	 * @see android.widget.ListAdapter#isSeparator(int)
	 */
	@Override
	public final boolean isEnabled(int position)
	{
		if (!mSeparators)
		{
			return true;
		}

		int numSections = mSections.size();
		for (int i = 0; i < numSections; i++)
		{
			ArrayList<E> section = mSections.get(i);
			int sectionSize = section.size();
			if (sectionSize == 1)
			{
				// The section only contains a separator and nothing else, skip
				// it
				continue;
			}
			if (position == 0)
			{
				// The first item in a section is always the separator
				return false;
			}
			position -= sectionSize;
		}
		return true;
	}

	/**
	 * @see android.widget.ListAdapter#getItem(int)
	 */
	public final Object getItem(int position)
	{
		return getEntry(mSections, position, mSeparators);
	}

	/**
	 * Get the entry for the given position.
	 * 
	 * @param sections
	 *            the list of sections
	 * @param position
	 *            the position for the desired entry
	 * @return the ContactEntry for the given position
	 */
	public final static <T extends Entry> T getEntry(
			ArrayList<ArrayList<T>> sections, int position, boolean separators)
	{
		int numSections = sections.size();
		for (int i = 0; i < numSections; i++)
		{
			ArrayList<T> section = sections.get(i);
			int sectionSize = section.size();
			if (separators && sectionSize == 1)
			{
				// The section only contains a separator and nothing else, skip
				// it
				continue;
			}
			if (position < section.size())
			{
				return section.get(position);
			}
			position -= section.size();
		}
		return null;
	}

	/**
	 * Get the count of entries in all sections
	 * 
	 * @param sections
	 *            the list of sections
	 * @return the count of entries in all sections
	 */
	public static <T extends Entry> int countEntries(
			ArrayList<ArrayList<T>> sections, boolean separators)
	{
		int count = 0;
		int numSections = sections.size();
		for (int i = 0; i < numSections; i++)
		{
			ArrayList<T> section = sections.get(i);
			int sectionSize = section.size();
			if (separators && sectionSize == 1)
			{
				// The section only contains a separator and nothing else, skip
				// it
				continue;
			}
			count += sections.get(i).size();
		}
		return count;
	}

	/**
	 * @see android.widget.ListAdapter#getItemId(int)
	 */
	public final long getItemId(int position)
	{
		Entry entry = getEntry(mSections, position, mSeparators);
		if (entry != null)
		{
			return entry.id;
		} else
		{
			return -1;
		}
	}

	/**
	 * @see android.widget.ListAdapter#getView(int, View, ViewGroup)
	 */
	public View getView(int position, View convertView, ViewGroup parent)
	{
		View v;
		if (convertView == null)
		{
			v = newView(position, parent);
		} else
		{
			v = convertView;
		}
		bindView(v, getEntry(mSections, position, mSeparators));
		return v;
	}

	/**
	 * Create a new view for an entry.
	 * 
	 * @parent the parent ViewGroup
	 * @return the newly created view
	 */
	protected abstract View newView(int position, ViewGroup parent);

	/**
	 * Binds the data from an entry to a view.
	 * 
	 * @param view
	 *            the view to display the entry in
	 * @param entry
	 *            the data to bind
	 */
	protected abstract void bindView(View view, E entry);
}
